local turtle = {}

---@class TurtleUnitSource
---@field name string 名字
---@field origin string 原话
---@field display string 展示
---@field importance string[] 关键词
---@field is_found string[] 已找到关键词
---@field complete boolean 是否完成

---@class TurtleUnit
---@field title string 标题
---@field origin string 汤面
---@field source TurtleUnitSource[] 线索


-- 辅助函数：将 UTF-8 字符串分割成字符表
local function utf8_to_table(s)
    local chars = {}
    local i = 1
    while i <= #s do
        local c = string.byte(s, i)
        local len = 1
        if c >= 240 then     -- 四字节字符 (U+10000 以上)
            len = 4
        elseif c >= 224 then -- 三字节字符 (大部分中文)
            len = 3
        elseif c >= 192 then -- 双字节字符
            len = 2
        end
        table.insert(chars, string.sub(s, i, i + len - 1))
        i = i + len
    end
    return chars
end

-- 辅助函数：在字符表中查找子序列
local function find_subsequence(t1, t2)
    local n, m = #t1, #t2
    if m == 0 or m > n then return nil end

    for i = 1, n - m + 1 do
        local match = true
        for j = 1, m do
            if t1[i + j - 1] ~= t2[j] then
                match = false
                break
            end
        end
        if match then
            return i, i + m - 1 -- 返回起始和结束位置
        end
    end
    return nil
end

---获取字符数量
function CountChar(str, char)
    -- 通过替换统计字符出现次数
    local _str = utf8_to_table(str)
    local count = 0
    for _, value in ipairs(_str) do
        if value == char then
            count = count + 1
        end
    end
    return math.tointeger(count)
end

---夹逼函数
---@param value integer
---@param min integer
---@param max integer
---@return integer
function Clamp(value, min, max)
    local lower_bound = math.min(min, max)
    local upper_bound = math.max(min, max)
    local result = math.tointeger(math.floor(
        math.min(
            math.max(value, lower_bound),
            upper_bound
        )
    )) ---@type integer

    ---@diagnostic disable-next-line: return-type-mismatch
    return result
end

-- 主函数
function ReplaceSubstring(original, mask, input)
    -- 分割字符表
    local t_ori = utf8_to_table(original)
    local t_mask = utf8_to_table(mask)
    local t_input = utf8_to_table(input)

    -- 基础校验
    if #t_ori ~= #t_mask then
        return mask -- 原字符串与掩码长度不一致
    end
    if #t_input == 0 or #t_input > #t_ori then
        return mask -- 输入为空或过长
    end

    -- 查找匹配位置
    local start_idx, end_idx = find_subsequence(t_ori, t_input)
    if not start_idx then
        return mask -- 无匹配
    end

    -- 替换掩码对应位置
    for i = start_idx, end_idx do
        t_mask[i] = t_input[i - start_idx + 1]
    end

    return table.concat(t_mask)
end

-- 判断两个集合是否相等（忽略顺序，允许重复元素）
function SetsEqual(a, b)
    -- 如果长度不同，直接返回 false
    if #a ~= #b then
        return false
    end

    -- 统计元素出现次数
    local count = {}
    for _, v in ipairs(a) do
        count[v] = (count[v] or 0) + 1
    end

    -- 遍历第二个集合，检查元素是否存在且次数匹配
    for _, v in ipairs(b) do
        if not count[v] or count[v] <= 0 then
            return false
        end
        count[v] = count[v] - 1
    end

    -- 检查所有元素是否归零（防止遗漏）
    for _, v in pairs(count) do
        if v ~= 0 then
            return false
        end
    end

    return true
end

---判断val是否在里
function IsInArray(_table, _val)
    for _, v in ipairs(_table) do
        if v == _val then
            return true
        end
    end
    return false
end

---@class Turtle
---@field title string 标题
---@field origin string 汤面
---@field source TurtleUnitSource[] 线索集
---@field sourceIndex table
---@field tip_index integer 当前提示索引
---@field complete boolean 是否完成
turtle.Turtle = {}
turtle.Turtle.__index = turtle.Turtle


do
    ---构造函数
    ---@return Turtle
    function turtle.Turtle.new()
        local self = setmetatable({}, turtle.Turtle)
        self.origin = ""
        self.source = {}
        self.tip_index = 0
        return self
    end

    ---检查是否完成
    ---@return boolean
    function turtle.Turtle:check()
        for _, value in ipairs(self.source) do
            if #value.importance ~= 0 and (not value.complete) then
                return false
            end
        end
        return true
    end

    ---解锁提示
    function turtle.Turtle:unlock()
        if (self.tip_index > 0) and rawget(self.source, self.tip_index) then
            local unit = self.source[self.tip_index]
            unit.display = unit.origin
            unit.complete = true
        end
    end

    ---查找关键词
    ---@param key string 关键词
    ---@return string
    function turtle.Turtle:find(key)
        local result = "不含"
        for _, unit in ipairs(self.source) do
            if not unit.complete then
                local importance = unit.importance
                local _last_display = unit.display
                unit.display = ReplaceSubstring(unit.origin, unit.display, key)
                if (_last_display ~= unit.display) and (result == "不含") then
                    result = "包含"
                end
                if (unit.display == unit.origin) then
                    if (result == "不含") or (result == "包含") then
                        result = "整句"
                    end
                    unit.complete = true
                elseif IsInArray(importance, key) then
                    result = "关键"
                    if not IsInArray(unit.is_found, key) then
                        table.insert(unit.is_found, key)
                    end
                    if SetsEqual(unit.importance, unit.is_found) then
                        unit.display = unit.origin
                        unit.complete = true
                    end
                end
            end
        end
        return result
    end

    ---展示文本携带提示
    ---@param _index integer
    ---@return string
    function turtle.Turtle:display_with_tip(_index)
        self.tip_index = Clamp(_index, 0, #self.source)
        local str = self.origin:gsub(
            "%%%((.-)%)",
            function(__index)
                local index = self.sourceIndex[__index]
                local value = self.source[self.sourceIndex[__index]]
                if #value.importance == 0 then
                    if index == self.tip_index then
                        return "#f(c:ffff00|o:999900|O:2)" .. value.display .. "#c4d4543"
                    else
                        return "#cffff00" .. value.display .. "#c4d4543"
                    end
                else
                    if index == self.tip_index then
                        return "#f(c:ff0000|o:990000|O:2)" .. value.display .. "#c4d4543"
                    else
                        return "#cff0000" .. value.display .. "#c4d4543"
                    end
                end
            end
        )
        return str
    end

    ---展示文本
    ---@return string
    function turtle.Turtle:display()
        return self:display_with_tip(0)
    end

    ---展示所需蘑菇数
    ---@return integer
    function turtle.Turtle:get_mush_count()
        local value = self.source[self.tip_index]
        if #value.importance == 0 then
            return CountChar(value.display, "□") * 2
        else
            return CountChar(value.display, "□") * 10
        end
    end

    ---下一提示索引
    ---@return string
    function turtle.Turtle:next_tip()
        local length = #self.source
        repeat
            self.tip_index = Clamp(self.tip_index + 1, 1, length)
        until (not self.source[self.tip_index].complete) or self.tip_index == length
        if self.source[self.tip_index].complete then
            return "none"
        end
        return self:display_with_tip(self.tip_index)
    end

    ---上一提示索引
    ---@return string
    function turtle.Turtle:last_tip()
        local length = #self.source
        repeat
            self.tip_index = Clamp(self.tip_index - 1, 1, length)
        until (not self.source[self.tip_index].complete) or self.tip_index == 1
        if self.source[self.tip_index].complete then
            return "none"
        end
        return self:display_with_tip(self.tip_index)
    end

    ---重置海龟汤
    function turtle.Turtle:reset()
        for _, value in ipairs(self.source) do
            value.is_found = {}
            value.complete = false
            ---@diagnostic disable-next-line: param-type-mismatch
            value.display = string.rep("□", math.tointeger(string.len(self.origin) // 3))
        end
    end
end

---@class TurtleList
---@field count integer 海龟汤数量
---@field __content Turtle[] 海龟汤列表
turtle.TurtleList = {}
turtle.TurtleList.__index = turtle.TurtleList

do
    ---构造函数
    ---@return TurtleList
    function turtle.TurtleList.new()
        local self = setmetatable({}, turtle.TurtleList)
        self.__content = {}
        return self
    end

    ---导入海龟汤
    ---@param path_to string
    function turtle.TurtleList:import(path_to)
        local content = require(string.format("turtle@%s", path_to)) ---@type TurtleUnit[]
        local __list = self.__content
        ---创建海龟单元
        for _, unit in ipairs(content) do
            local _turtle = turtle.Turtle.new()
            local _sourceIndex = {}
            _turtle.title = unit.title
            _turtle.origin = unit.origin
            _turtle.source = unit.source
            for index, value in ipairs(_turtle.source) do
                ---@diagnostic disable-next-line: param-type-mismatch
                local _display = string.rep("□", math.tointeger(string.len(value.origin) // 3))
                value.display = _display
                value.is_found = {}
                value.complete = false
                _sourceIndex[value.name] = math.tointeger(index)
            end
            _turtle.sourceIndex = _sourceIndex
            table.insert(__list, _turtle)
        end
    end

    ---索引方法
    ---@param __index integer
    ---@return Turtle
    function turtle.TurtleList:index(__index)
        return self.__content[__index]
    end

    ---迭代海龟汤
    function turtle.TurtleList:__pairs()
        return next, self.__content, nil
    end
end

return turtle
